iT邦幫忙

2024 iThome 鐵人賽

DAY 12
0
生成式 AI

2024 年用 LangGraph 從零開始實現 Agentic AI System系列 第 12

【Day 12】- AI代理自我反思:深入探討 Self-Refine 技術與 LangGraph 實作

  • 分享至 

  • xImage
  •  

摘要
這篇文章主要探討了 大型語言模型 (LLM) 的自我完善技術,特別是 Self-Refine 的概念和實作方法。文章從介紹 Reflection Agents 的概念出發,解釋了它們如何透過 自我反思 來提升模型的輸出質量。接著,文章詳細闡述了 Self-Refine 的工作原理,包含三個核心步驟:問題識別、建議生成、上下文整合與程式碼重寫。
文章中還介紹了 Self-Refine 的 框架組成 和 實驗結果,證明了它在不同任務中相較於傳統方法的優勢。最後,文章使用 LangGraph 來實際示範如何實現 Self-Refine 技術,並深入探討了 迭代次數、模型能力 等影響因素。文章結尾提到了更進階的 Reflexion 技術,並展望了 LLM 自我完善技術的發展方向。

引言:LLM 的自我完善之路

相比於過往單一 Workflow Agent 開發,當今人工智慧導入了 Agent 概念,可借助大型語言模型力量,對於程式運作尚有機會達到逐步改良的機會。如果在 Agent 當中加入自動回饋,好讓模型在步驟中對於自己的輸出進行一輪批判並且給予回應,那會如何呢?這就是為何需要 Reflection。

表達 Reflection 機制

本文探討了 LLM 自我優化的基礎技術 Self-Refine。這是 AI 代理反思機制的入門級實現,為更進階的技術如 Reflexion 奠定了基礎。在後續的文章中,我們將深入探討 Reflexion 如何在 Self-Refine 的基礎上進一步提升 LLM 的自我完善能力。

1. 什麼是 Reflection Agents?💡

Reflection Agents 是一種先進的 AI 代理,能夠對自身的輸出進行批判性分析和自我改進。這種機制使 AI 系統能夠像人類一樣,通過反思和迭代來不斷優化自己的表現。

一句話定義:Reflection 是一種提示策略,旨在提高 AI 代理和類似系統的輸出質量和成功率。

相較於傳統的單一 Workflow Agent 開發,Reflection Agents 利用大型語言模型(LLM)的強大能力,實現了程序運行的逐步改良。這種方法可以顯著改善 ChatGPT、Claude、Gemini 和 Llama 等模型的輸出質量。

2. Reflection 機制的核心步驟

圖片:Reflection 機制的核心步驟流程圖

2.1 問題識別與建議生成:LLM 的自我批評能力

Self-Refine 的第一步是讓 LLM 對自身的輸出進行批判性分析。這個過程中,LLM 不僅要識別潛在的問題,還要生成具體的改進建議。例如,對於一篇文章評論,LLM 可能會指出"情感傾向不夠明確"或"缺乏具體細節支撐觀點"等問題。這種自我批評的能力是 Self-Refine 技術的基石,為後續的優化提供了明確的方向。

2.2 上下文整合與程式碼重寫:從反饋到行動

在識別問題並生成建議後,Self-Refine 的下一步是將這些反饋與初始輸出整合為新的上下文。基於這一綜合信息,LLM 進行代碼或內容的重寫。這個過程不僅僅是簡單的修改,而是對整體內容進行深思熟慮的重構,以確保改進建議被有效地融入新的輸出中。

2.3 迭代優化:持續提升輸出品質的關鍵

Self-Refine 的核心在於其迭代性。通過反覆執行問題識別、建議生成和內容重寫的步驟,LLM 能夠不斷提升輸出質量。研究表明,每次迭代都可能帶來顯著的性能提升,尤其是在初始幾輪迭代中。這種持續優化的過程使得 Self-Refine 在代碼生成、文本創作和問答系統等多個領域都展現出卓越的效果。

亮點:這種自我反思和迭代優化的過程,在代碼生成、文本創作和問答系統等多個領域都展現出卓越的效果,大大提升了 LLM 的實用性和可靠性。

3. Self-Refine:LLM 自我優化的創新方法

讓我們深入探討 Self-Refine 的核心理念、工作原理和框架組成。

3.1 Self-Refine 的核心理念:無需額外訓練的迭代優化

Self-Refine 是一種創新的 LLM (大型語言模型) 優化技術,通過迭代式自我回饋來提升模型在多樣化任務上的表現。這種方法無需額外的監督訓練數據或強化學習,僅依靠單一 LLM 即可實現。

一般來說,Self-Refine 在實作中會被稱為 Basic Reflection 技巧,但這邊還是希望大家心從源頭認識與了解核心重點。

補上 論文架構圖

3.2 工作原理解析:從初始輸出到優化結果

Self-Refine 的工作原理基於三個關鍵步驟,形成一個完整的改善循環:

  1. 初始輸出生成:
    • LLM 根據給定的提示或任務生成初始回應。
    • 這個步驟為後續的優化過程提供了基礎材料。
  2. 自我回饋:
    • LLM 對自身的輸出進行評估,識別潛在問題。
    • 生成具體、可操作的改進建議。
    • 這一步驟體現了 LLM 的自我批評能力,是 Self-Refine 的核心所在。
  3. 優化:
    • 根據自我回饋,LLM 修改並改進初始輸出。
    • 這個過程不僅僅是簡單的修正,而是對內容進行全面的重構和優化。

這三個步驟可以進行多輪迭代,每次迭代都有可能帶來顯著的性能提升。

關鍵優勢: 無需額外訓練,單一 LLM 即可完成全流程

3.3 框架組成:FEEDBACK、REFINE 和迭代機制的協同作用

Self-Refine 框架由三個主要組件構成,它們協同工作,形成一個強大的迭代改善循環:

  1. FEEDBACK 模組:

    • 功能:評估當前輸出並提供多維度、可操作的回饋。
    • 特點:回饋涵蓋多個方面,如內容準確性、語言流暢度、邏輯連貫性等。
    • 範例:對於一篇評論,可能會指出"情感傾向不夠明確"或"缺乏具體細節支撐觀點"。
  2. REFINE 模組:

    • 功能:根據 FEEDBACK 模組的建議優化輸出。
    • 特點:智慧解析回饋,針對性地修改內容。
    • 範例:根據回饋"增加正面情感表達",將"不錯的產品"改為"令人驚喜的優質產品"。
  3. 迭代機制:

    • 功能:控制 FEEDBACK → REFINE 循環的執行。
    • 特點:根據預設條件或質量閾值決定是否繼續迭代。
    • 重要性:確保輸出質量持續提升,同時避免無限循環。

這三個組件的緊密配合,使 Self-Refine 能夠不斷提升輸出質量,直至達到預期標準。框架的靈活性還允許根據具體任務需求進行調整,使其適用於各種不同的應用場景。

框架實現示例

以下是 Self-Refine 框架的簡化 Python 實現示例:

def self_refine(prompt: str, max_iterations: int = 3) -> str:
    def is_refinement_sufficient(prompt, feedback, initial, refined) -> bool:
        # 實現任務特定的停止條件
        pass

    answer = ChatGPT(prompt)  # 初始輸出生成

    for _ in range(max_iterations):
        feedback = ChatGPT(feedback_prompt, answer)  # 生成回饋
        refined = ChatGPT(refiner_prompt, feedback, answer)  # 根據回饋優化

        if is_refinement_sufficient(prompt, feedback, answer, refined):
            break

        answer = refined  # 更新答案為優化後的版本

    return refined

實驗結果:Self-Refine 真的能超越傳統方法嗎?

為了驗證 Self-Refine 的效果,研究者們設計了一系列全面的實驗。這些實驗旨在回答幾個關鍵問題:

研究者們選擇了 7 種不同類型的任務,包含:情感反轉(Sentiment Reversal)、對話回應生成(Dialogue Response Generation)、代碼優化(Code Optimization)、代碼可讀性改進(Code Readability Improvement)、數學推理(Math Reasoning)、首字母縮略詞生成(Acronym Generation)、約束生成(Constrained Generation)等7 個不同的任務進行了廣泛的實驗,證明Self-Refine 的性能優於GPT- 3.5 甚至GPT- 等強大生成器的直接生成。
補上任務描述
image caption: 用於評估 SELF-REFINE 的任務概覽,包括相關數據集及其規模。對於每項任務,作者展示了一次迭代優化的過程,包括輸入 x、先前生成的輸出 y_t、生成的反饋 f_bt,以及優化後的結果 y_t+1

主要結果:

補上 Table1

實驗數據揭示,Self-Refine 技術在各種規模的模型上均表現出色:

  1. 程式碼最佳化:GPT-4 + Self-Refine 相比基礎 GPT-4 模型提升了 8.7 個百分點,最佳化比例從 27.3% 躍升至 36.0%。
  2. 對話回應生成:GPT-4 的偏好分數驚人提升 49.2%,從 25.4% 飆升至 74.6%。
    值得注意的是,GPT-4 + Self-Refine 在所有任務中都優於 GPT-3.5 和 ChatGPT 的 Self-Refine 版本,即使在某些任務中 GPT-4 的初始表現不如其他模型。

這一現象表明,Self-Refine 有效釋放了更強大模型的潛力。

4. Self-Refine 的實驗分析:

Self-Refine 技術的核心在於其三大步驟:FEEDBACK(回饋)、REFINE(優化)以及這兩個步驟的迭代循環。作者為當中步驟進行了些探討。

4.1 迭代幾次才是好的呢?

Self-Refine 的核心優勢之一是其迭代優化能力。研究團隊仔細探討了多輪 FEEDBACK-REFINE 迭代對性能的影響,得出了一些令人振奮的結果:

  • 程式碼最佳化:初始分數 22.0,三次迭代後提升至 28.8
  • 情感反轉:初始分數 33.9,三次迭代後達到 36.8
  • 約束生成:初始分數 29.0,三次迭代後飆升至 49.7

Figure 4:插入圖表:不同任務在迭代過程中的性能提升曲線

研究發現,雖然性能隨迭代次數增加而提升,但存在邊際效應遞減的現象。值得注意的是,在多方面回饋任務(如首字母縮略詞生成)中,性能可能不會單調增加,這凸顯了平衡評估的重要性。

4.2 模型能力的重要性:為什麼較弱模型難以受益

Self-Refine 的效果與基礎模型的能力密切相關。研究團隊使用 Vicuna-13B 模型進行的實驗揭示了一些關鍵洞察:

  1. Vicuna-13B 能夠生成初始輸出,但在優化過程中遇到顯著困難
  2. 模型難以一致地生成所需格式的回饋
  3. 即使提供預設回饋,也常常無法按提示進行優化

這一發現表明,Self-Refine 的效能與基礎模型的能力密切相關,特別是模型對指令的理解和執行能力。

5. 動手實踐:使用 LangGraph 實現 Self-Refine

理論學習固然重要,但實際動手實現 Self-Refine 可以讓我們更深入地理解這項技術。在這一部分,我們將使用 LangGraph 來實現 Self-Refine,並通過一個具體案例來展示整個過程。

5.1 步驟一:搭建基本框架

首先,我們需要建立一個穩固的基礎架構。這包括導入必要的模組、設定 API 金鑰,以及初始化 ChatOpenAI 模型。

# 設定 OpenAI API 金鑰
os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY')

# 初始化 ChatOpenAI
llm = ChatOpenAI(
    temperature=0.7,
    max_tokens=1024,
    streaming=True
)

在此段程式碼中,我們首先導入所需的模組。其中,使用 ChatOpenAI 作為我們的語言模型,並設定其參數。temperature 設為 0.7,這提供了一個平衡點,既保留了一定的創造性,又不會過於發散。streaming=True 則允許我們即時獲取生成的內容。

📌 實用提示:確保所有環境變數都正確設置,這對於順利運行後續程式碼至關重要。

5.2 步驟二:實現 FEEDBACK 和 REFINE 模組

接下來,我們定義兩個核心模組:生成(REFINE)和反思(FEEDBACK)。這兩個模組構成了 Self-Refine 的基本循環。

# 生成模組(REFINE)
prompt = ChatPromptTemplate.from_messages([
    ("system", "您是一位專業的論文助理,擅長撰寫出色的五段式論文。"
              "請為使用者的要求生成最佳論文。"
              "如果使用者提供批評,請根據先前的嘗試提供修訂版本。"),
    MessagesPlaceholder(variable_name="messages"),
])
generate = prompt | llm

essay = ""
request = HumanMessage(
    content="請寫一篇論文,探討《哈利波特》在現代童年中的相關性"
)
for chunk in generate.stream({"messages": [request]}):
    print(chunk.content, end="")
    essay += chunk.content

生成結果

# 反思模組(FEEDBACK)
reflection_prompt = ChatPromptTemplate.from_messages([
    ("system", "您是一位正在評分論文的老師。請為使用者的作品提供批評和建議。"
              "提供詳細的建議,包括長度、深度、風格等方面的要求。"),
    MessagesPlaceholder(variable_name="messages"),
])
reflect = reflection_prompt | llm

reflection = ""
for chunk in reflect.stream({"messages": [request, HumanMessage(content=essay)]}):
    print(chunk.content, end="")
    reflection += chunk.content

反思結果

在這兩段程式碼中,我們定義了兩個不同的提示模板。生成模組負責創作內容,而反思模組則負責評估和提供改進建議。這種分離設計允許系統在不同階段專注於不同的任務,從而提高整體效能。MessagesPlaceholder 用於動態插入對話歷史,確保模型能夠根據上下文進行回應。

📌 小節摘要:
定義生成(REFINE)模組,負責內容創作
定義反思(FEEDBACK)模組,負責評估和提供建議

5.3 步驟三:設計迭代機制

現在,我們設計 AI 的決策邏輯和狀態管理機制。這個機制決定了 AI 如何在生成和反思之間切換,以及何時結束迭代過程。
這邊開始就可以仰賴 LangGraph 協助完成。

class State(TypedDict):
    messages: Annotated[List[BaseMessage], add_messages]

async def generation_node(state: State) -> State:
    response = await generate.ainvoke(state["messages"])
    return {"messages": state["messages"] + [AIMessage(content=response.content)]}

async def reflection_node(state: State) -> State:
    cls_map = {"ai": HumanMessage, "human": AIMessage}
    messages = state["messages"]
    translated = [messages[0]] + [cls_map[msg.type](content=msg.content) for msg in messages[1:]]
    res = await reflect.ainvoke({"messages": translated})
    return {"messages": state["messages"] + [HumanMessage(content=res.content)]}

def should_continue(state: State) -> str:
    if len(state["messages"]) > 6:
        return END
    return "reflect"

這段程式碼定義了系統的狀態結構和運作邏輯:

  1. State 類別定義了系統狀態,主要包含消息歷史。
  2. generation_node 函數負責生成新內容,並將其添加到狀態中。
  3. reflection_node 函數負責反思和提供建議。注意這裡的消息類型轉換,這是為了模擬人類反饋。
  4. should_continue 函數決定是否繼續迭代。這裡設置了一個簡單的條件:當消息數量超過 6 時停止,這可以根據實際需求進行調整。

📌 小節摘要:

  • 定義狀態結構,用於追踪對話歷史
  • 實現生成和反思節點,分別負責內容創作和評估
  • 設計迭代終止條件,控制自我完善的次數

5.4 建構圖,並與之互動

# 建構圖
builder = StateGraph(State)
builder.add_node("generate", generation_node)
builder.add_node("reflect", reflection_node)
builder.add_edge(START, "generate")
builder.add_conditional_edges("generate", should_continue)
builder.add_edge("reflect", "generate")
graph = builder.compile()

上 LangGraph 圖
這段程式碼展示了如何構建和運行 Self-Refine 系統:

  1. 使用 StateGraph 建立計算圖,定義節點和邊的關係。
  2. 添加條件邊,實現迭代邏輯。
  3. 編譯圖,準備執行。
  4. 最後,輸出優化後的結果。
# 運行圖
async for event in graph.astream({"messages": [HumanMessage(content="生成一篇論文,探討《小王子》及其訊息在現代生活中的時代性")]}):
    print(event)
    print("---")

# 列印最終結果
import rich
rich.print(event['generate']['messages'][-1].content)

上執行結果

6. 總結與展望

本章節詳細介紹了如何使用 LangGraph 實現 Self-Refine 技術,這是 LLM 自我優化的基礎技術。通過精心設計的生成和反思模組,以及靈活的狀態管理和決策邏輯,我們成功構建了一個能夠自我完善的 AI 系統。

6.1 Self-Refine 的核心優勢

  1. 自主學習:系統能夠透過反覆的生成和反思來提升輸出質量。
  2. 靈活性:可以根據不同任務需求調整提示和迭代邏輯。
  3. 可擴展性:這個框架可以輕易地應用到各種不同的任務中。

6.2 從 Self-Refine 到 Reflexion

Self-Refine 作為 LLM 反思機制的入門級實現,為更進階的技術如 Reflexion 奠定了重要基礎。Reflexion 在 Self-Refine 的基本原理上進行了創新和擴展,引入了更複雜的機制:

  1. 記憶機制:Reflexion 引入了長期記憶,使 LLM 能夠從過去的經驗中學習。
  2. 多步推理:相比 Self-Refine 的單一反思-生成循環,Reflexion 支持更複雜的多步驟推理過程。
  3. 任務適應性:Reflexion 能夠根據不同任務的特性動態調整其反思策略。

6.3 後續學習路徑

為了更全面地理解 LLM 的自我完善能力,我們建議讀者在學習完 Self-Refine 後,進一步探索 Reflexion 技術。在下一篇文章中,我們將深入探討 Reflexion 如何在 Self-Refine 的基礎上進一步提升 LLM 的自我完善能力,包括其創新的記憶機制和多步推理過程。
通過比較 Self-Refine 和 Reflexion,讀者將能夠更好地理解 LLM 反思機制的演進過程,以及這些技術如何推動 AI 系統向更高層次的智能發展。

即刻前往教學程式碼 Repo,親自動手體驗反思的威力吧!別忘了給專案按個星星並持續關注更新,讓我們一起探索AI代理的新境界。

X.參考資料


上一篇
【Day 11】- 從反思到監督:五大 AI 代理設計模式速成指南
下一篇
【Day 13】- 進階 LLM 反思機制:Reflexion 技術的創新與應用
系列文
2024 年用 LangGraph 從零開始實現 Agentic AI System31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言